<?php
/**
 * Parcl
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://parcl.com/license
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@parcl.com so we can send you a copy immediately.
 *
 * @category parcl
 * @package parcl-core
 * @copyright Copyright (c) 2006-2011 Mikael Knutsson (http://mikn.me)
 * @license http://parcl.com/license New BSD License
 * 
 * @author mikael
 */

namespace Parcl\Core {

	if(PARCL_EXEC !== 1) {
		die('<h1>Incorrect Access</h1>You cannot access this file directly.<br/>' .
			 'If you got this error message by clicking a link on the site,' .
			 'please notify the webmaster as soon as possible.');
	}

	/**
	 * Purpose     : Collect all events in the same place to be easily checked on.
	 * Description : This class contains all events that can be triggered, made a
					 singleton so that it can't be instanciated more than once and
					 having more than one collection of events.
	 * - Construct -
	 * Input       : None.
	 * Description : Makes $this->events into an ArrayObject.
	 * Errors      : None.
	 * Assumptions : None.
	 * Dev Notes   : None at the moment.
	 */

	class EventCollection implements ISingleton {
		private $events;
		private static $lock = false;
		private static $instance = null;

		public static function GetInstance() {
			if(is_null(self::$instance)) {
				self::$instance = new EventCollection();
			}
			return self::$instance;
		}

		private function __construct() {
			$this->events = new ArrayObject();
		}

		/**
		 * Input       : $event: An event to add to the collection.
						 $static: A boolean saying if the Event is static or not.
		 * Output      : Returns boolean true upon successful execution.
		 * Description : This function adds an Event and its conditions to the collection
						 if it passes all conditions.
		 * Errors      : An error is thrown if $event isn't an Event, the EventCollection
						 has been announced locked or if it is already in the collection
						 an notice-level exception is thrown. An error is thrown if
						 you pass a value to $static that isn't a boolean too.
		 * Assumptions : None.
		 * Dev Notes   : Pretty straight-forward function.
		 */

		public function Add($event, $static = true) {
			Event::IsEvent($event);
			if(self::$lock == true) {
				throw new EventException('Tried to add new events after event lock '
										  . 'was in place. Event name: ' . $event,
										  EventException::EVENT);
			}
			if($this->Contains($event)) {
				throw new EventException('Tried to add an already existing event.',
										  EventException::EVENT_NOTICE);
			}
			if(!is_bool($static)) {
				throw new EventException('Tried passing a variable through $static' .
										  'that isn\'t a boolean.', EventException::EVENT);
			}
			$this->events->Append(array('name' => $event, 'NS' => $event->GetNamespace(),
										'static' => $static,
										'triggered' => false));
			return true;
		}

		/**
		 * Input       : $event: An event to check if it exists.
		 * Output      : Boolean true if the event exists, false otherwise.
		 * Description : Checks if the specific event exists in the EventCollection or not.
		 * Errors      : Indirectly throws an error if $event isn't an Event.
		 * Assumptions : None.
		 * Dev Notes   : None at the moment.
		 */

		public function Contains($event) {
			Event::IsEvent($event);
			foreach($this->events as $events) {
				if($events['name'] == $event) {
					return true;
				}
			}
			return false;
		}

		/**
		 * Input       : $event: An event to flag as triggered.
		 * Output      : Boolean true if the event exists, false otherwise.
		 * Description : Flags a specific event as triggered.
		 * Errors      : Throws an error if the event up for flagging doesn't exist.
		 * Assumptions : None.
		 * Dev Notes   : Added this to make the event system more logical, this was
						 an array in EventHandlerCollection before.
		 */

		public function SetFlagTriggered($event) {
			Event::IsEvent($event);
			foreach($this->events as &$events) {
				if($events['name'] == $event) {
					$events['triggered'] = true;
					return true;
				}
			}
			throw new EventException('Event up for flagging as triggered does not exist.',
									 EventException::EVENT);
			return false;
		}

		/**
		 * Input       : None.
		 * Output      : $expiredEvents: Events that have been triggered.
		 * Description : Fetches all triggered events.
		 * Errors      : None.
		 * Assumptions : None.
		 * Dev Notes   : Added this to make the event system more logical, this was
						 an array in EventHandlerCollection before.
		 */

		public function GetFlagTriggered($onlyExpired = true) {
			$expiredEvents = array();
			foreach($this->events as $event) {
				if($event['triggered']) {
					if(($onlyExpired && $event['static']) || !$onlyExpired)
					$expiredEvents[] = $event['name'];
				}
			}
			return $expiredEvents;
		}

		/**
		 * Input       : $namespace: The namespace you want to reset triggered status on.
		 * Output      : Boolean true if successfully completed.
		 * Description : Resets triggered status on a given namespace.
		 * Errors      : Throws an error if the namespace has no defined events.
		 * Assumptions : That $namespace is a strung.
		 * Dev Notes   : I knew that namespaces would be useful on events!
		 */

		public function ResetTriggeredNS($namespace) {
			$resetEvents = array();
			foreach($this->events as &$event) {
				if($event['static'] == false && $event['NS'] == $namespace) {
					$event['triggered'] = false;
					$resetEvents[] = $event['name'];
				}
			}
			if(empty($resetEvents)) {
				throw new EventException('Namespace is not defined.', EventException::NS);
			}
			return $resetEvents;
		}

		/**
		 * Input       : None.
		 * Output      : No direct output.
		 * Description : Locks the EventCollection from further events.
		 * Errors      : If it already has been locked, it throws a notice level exception.
		 * Assumptions : None.
		 * Dev Notes   : Made this as a security measure to make sure you can't add
						 events after a certain event, like when the first event is triggered,
						 or somesuch.
		 */

		public static function Lock() {
			if(self::$lock === true) {
				throw new EventException('Tried to lock an already locked collection.',
										  EventException::EVENT_NOTICE);
			}
			self::$lock = true;
			return true;
		}
	}
}